import { defineComponent, ref, toRef, computed, reactive, watch, onMounted, onUnmounted, watchEffect, provide, createVNode } from 'vue';
import CarouselItem from './Item.js';
import InnerItem from './InnerItem.js';
import { useStyle } from '../use/useStyle.js';
import Space from '../Space/index.js';
import { FeatherChevronLeft } from 'cui-vue-icons/feather';

const CarouselContextKey = Symbol('CarouselContextKey');
const Carousel = /* @__PURE__ */ defineComponent({
  name: 'Carousel',
  props: {
    height: {
      type: Number
    },
    arrow: {
      type: Boolean,
      default: false
    },
    autoPlay: {
      type: Boolean,
      default: false
    },
    duration: {
      type: Number,
      default: 4000
    },
    effect: {
      type: String,
      default: 'slide'
    },
    dotType: {
      type: String,
      default: 'dot'
    },
    dotAlign: {
      type: String
    },
    dotColor: {
      type: String,
      default: 'rgba(var(--cui-grey-8), 0.3)'
    },
    dotActiveColor: {
      type: String,
      default: 'rgba(var(--cui-white), 1)'
    },
    activeIndex: {
      type: Number
    },
    itemsPerView: {
      type: [Number, String],
      default: 1
    },
    gutter: {
      type: Number,
      default: 0
    },
    draggable: {
      type: Boolean
    },
    dir: {
      type: String,
      default: 'h'
    }
  },
  emits: ['change', 'update:activeIndex'],
  setup(props, {
    emit,
    slots
  }) {
    const activeIndex = ref(props.activeIndex ?? 0);
    const current = ref(activeIndex.value);
    const arrow = toRef(props, 'arrow');
    const dotType = toRef(props, 'dotType');
    const autoPlay = toRef(props, 'autoPlay');
    const duration = toRef(props, 'duration');
    const draggable = toRef(props, 'draggable');
    const effect = toRef(props, 'effect');
    const itemsPerView = toRef(props, 'itemsPerView');
    const dir = toRef(props, 'dir');
    const dotColor = toRef(props, 'dotColor');
    const dotActiveColor = toRef(props, 'dotActiveColor');
    const dotAlign = computed(() => props.dotAlign ?? (dir.value === 'h' ? 'bottom' : 'right'));
    const gutter = toRef(props, 'gutter');
    const classList = computed(() => ({
      'cm-carousel': true,
      [`cm-carousel-${effect.value}`]: effect.value,
      [`cm-carousel-${dir.value}`]: dir.value
    }));
    const wrap = ref();
    const list = ref();
    let playTimer = null;
    const arrowClasses = computed(() => ({
      'cm-carousel-arrow': true,
      [`cm-carousel-arrow-${arrow.value}`]: !!arrow.value
    }));
    const arrowsClasses = computed(() => ({
      'cm-carousel-actions': true,
      'cm-carousel-actions-with-arrow': arrow.value,
      [`cm-carousel-actions-${dotAlign.value}`]: !!dotAlign.value
    }));
    const dotClasses = computed(() => ({
      'cm-carousel-dots': true,
      [`cm-carousel-dots-${dotType.value}`]: !!dotType.value,
      [`cm-carousel-dots-${dotAlign.value}`]: !!dotAlign.value
    }));
    let inited = false;
    const store = reactive({
      data: [],
      activeIndex: activeIndex.value,
      unActiveIndex: -1,
      activeKey: '',
      unActiveKey: '',
      nextKey: '',
      prevKey: '',
      startPos: 0,
      isSwiping: false,
      currentPos: 0,
      dir: 'normal'
    });
    const views = ref(itemsPerView.value !== 'auto' ? new Array(Math.ceil(store.data.length / itemsPerView.value)).fill(1) : []);
    const loop = computed(() => effect.value === 'slide' && itemsPerView.value === 1 || effect.value === 'card' || effect.value === 'fade');
    const listStyle = ref({});
    const getWidth = () => {
      return list.value ? dir.value === 'h' ? list.value.getBoundingClientRect().width : list.value.getBoundingClientRect().height : 0;
    };

    // 添加元素
    const addItem = item => {
      item.index = store.data.length;
      store.data.push(item);
      views.value = itemsPerView.value !== 'auto' ? new Array(Math.ceil(store.data.length / itemsPerView.value)).fill(1) : [];
    };

    // 获取子元素的总长度
    const getItemsWidth = () => {
      if (list.value) {
        const items = list.value.querySelectorAll('.cm-carousel-item');
        const allW = Array.from(items).reduce((a, b) => {
          return a += dir.value === 'h' ? b.getBoundingClientRect().width : b.getBoundingClientRect().height;
        }, 0);
        return allW + gutter.value * (items.length - 1);
      }
      return 0;
    };
    const slideEffect = () => {
      const index = current.value;
      activeIndex.value;
      dir.value;
      if (index === views.value.length) {
        list.value.style.transitionDuration = '0ms';
        let offset = 0;
        if (store.isSwiping && store.activeIndex === views.value.length - 1) {
          offset = store.currentPos - store.startPos;
        }
        list.value.style.transform = dir.value === 'h' ? `translateX(${offset}px)` : `translateY(${offset}px)`;
      }
      if (index === -1) {
        const w = getWidth() + gutter.value;
        list.value.style.transitionDuration = '0ms';
        let offset = 0;
        if (store.isSwiping && store.activeIndex === 0) {
          offset = store.currentPos - store.startPos;
        }
        list.value.style.transform = dir.value === 'h' ? `translateX(${-(views.value.length + 1) * w + offset}px)` : `translateY(${-(views.value.length + 1) * w + offset}px)`;
      }
      transToActive();
    };
    const transToActive = () => {
      if (effect.value !== 'slide') {
        return;
      }
      setTimeout(() => {
        const index = activeIndex.value + (loop.value ? 1 : 0);
        const w = getWidth() + gutter.value;
        const duration = !inited ? 0 : 300;
        let offset = index * w;
        const allW = getItemsWidth();
        // 当前视窗超过了总长度
        if (offset + getWidth() > allW) {
          offset = offset - getWidth() + allW % getWidth();
        }
        const transform = dir.value === 'h' ? `translateX(${-offset}px)` : `translateY(${-offset}px)`;
        listStyle.value = {
          'gap': `${gutter.value}px`,
          'transition-duration': `${duration}ms`,
          'transform': transform
        };
        list.value.setAttribute('data-offset', -offset);
        inited = true;
      });
    };
    watch(() => [effect.value, current.value, activeIndex.value, dir.value, views.value], () => {
      if (!list.value) {
        return {};
      }
      if (effect.value === 'slide') {
        slideEffect();
      }
    });

    // 播放
    const play = () => {
      clearTimeout(playTimer);
      onNext();
      playTimer = setTimeout(() => {
        play();
      }, duration.value);
    };
    watch(() => props.activeIndex, val => {
      activeIndex.value = val;
    });
    onMounted(() => {
      if (wrap.value) {
        setTimeout(() => {
          if (itemsPerView.value === 'auto') {
            const w = getWidth();
            const allW = getItemsWidth();
            const viewsVal = Math.ceil(allW / w);
            if (w) {
              views.value = new Array(viewsVal).fill(1);
            }
          } else {
            views.value = new Array(Math.ceil(store.data.length / itemsPerView.value)).fill(1);
          }
        });
        if (autoPlay.value) {
          playTimer = setTimeout(() => {
            play();
          }, duration.value);
        }
      }
    });
    onUnmounted(() => {
      if (playTimer) {
        clearTimeout(playTimer);
      }
    });
    watchEffect(() => {
      store.unActiveIndex >= 0 && (effect.value === 'fade' || effect.value === 'card') ? store.unActiveKey = store.data[store.unActiveIndex].id : false;
    });
    watchEffect(() => {
      const index = activeIndex.value;
      store.activeIndex = index;
      if ((effect.value === 'fade' || effect.value === 'card') && store.data?.length) {
        store.prevKey = store.data[(views.value.length + index - 1) % views.value.length].id;
        store.nextKey = store.data[(index + 1) % views.value.length].id;
        store.activeKey = store.data[index].id;
      }
    });
    const onNext = () => {
      store.unActiveIndex = store.activeIndex;
      if (loop.value) {
        current.value = store.activeIndex + 1;
        activeIndex.value = (store.activeIndex + 1) % views.value.length;
      } else {
        const lastIndex = store.activeIndex;
        const cur = Math.min(lastIndex + 1, views.value.length - 1);
        current.value = cur;
        activeIndex.value = cur;
        if (cur === lastIndex && draggable.value) {
          transToActive();
        }
      }
      emit('change', activeIndex.value);
      emit('update:activeIndex', activeIndex.value);
    };
    const onPrev = () => {
      store.unActiveIndex = store.activeIndex;
      if (loop.value) {
        current.value = store.activeIndex - 1;
        activeIndex.value = (views.value.length + store.activeIndex - 1) % views.value.length;
      } else {
        const lastIndex = store.activeIndex;
        const cur = Math.max(store.activeIndex - 1, 0);
        current.value = cur;
        activeIndex.value = cur;
        if (cur === lastIndex && draggable.value) {
          transToActive();
        }
      }
      emit('change', activeIndex.value);
      emit('update:activeIndex', activeIndex.value);
    };
    const onDotClick = num => {
      store.unActiveIndex = store.activeIndex;
      current.value = num;
      activeIndex.value = num;
      emit('change', activeIndex.value);
      emit('update:activeIndex', activeIndex.value);
    };
    const onSwipe = swipe => {
      if (effect.value === 'slide') {
        listStyle.value = {
          'gap': `${gutter.value}px`,
          'transition-duration': `0ms`,
          'transform': dir.value === 'h' ? `translateX(${store.startPos - swipe.distanceX.value}px)` : `translateY(${store.startPos - swipe.distanceY.value}px)`
        };
      }
      store.currentPos = store.startPos - (dir.value === 'h' ? swipe.distanceX.value : swipe.distanceY.value);
    };
    const onSwipeStart = () => {
      const offset = list.value.getAttribute('data-offset');
      store.isSwiping = true;
      store.startPos = parseFloat(offset);
    };
    const onSwipeEnd = (direction, duration) => {
      if (duration && duration > 500) {
        effect.value === 'slide' ? transToActive() : false;
        return;
      }
      if (dir.value === 'h') {
        if (direction === 'right') {
          onPrev();
        } else if (direction === 'left') {
          onNext();
        } else {
          effect.value === 'slide' ? transToActive() : false;
        }
      }
      if (dir.value === 'v') {
        if (direction === 'down') {
          onPrev();
        } else if (direction === 'up') {
          onNext();
        } else {
          effect.value === 'slide' ? transToActive() : false;
        }
      }
      store.isSwiping = false;
    };
    const style = () => useStyle({
      height: (props.height ?? 250) + 'px'
    });
    provide(CarouselContextKey, {
      store,
      effect,
      itemsPerView,
      onSwipe,
      onSwipeStart,
      onSwipeEnd,
      draggable,
      addItem
    });
    return () => createVNode("div", {
      "class": classList.value,
      "style": style(),
      "ref": wrap
    }, [slots.default?.(), createVNode("div", {
      "class": arrowsClasses.value
    }, [createVNode("ul", {
      "class": dotClasses.value,
      "style": {
        "--cui-carousel-dot-color": dotColor.value,
        "--cui-carousel-active-dot-color": dotActiveColor.value
      }
    }, [views.value.map((item, index) => {
      const dotClass = () => ({
        'cm-carousel-dot': true,
        'cm-carousel-dot-active': store.activeIndex === index
      });
      return createVNode("li", {
        "class": dotClass(),
        "onClick": () => {
          onDotClick(index);
        }
      }, null);
    })]), arrow.value ? createVNode(Space, {
      "dir": dotAlign.value === 'bottom' || dotAlign.value === 'top' ? 'h' : 'v'
    }, {
      default: () => [createVNode("div", {
        "class": arrowClasses.value,
        "x-placement": "left",
        "onClick": onPrev
      }, [dir.value === 'h' ? createVNode(FeatherChevronLeft, null, null) : createVNode(FeatherChevronLeft, {
        "rotate": 90
      }, null)]), createVNode("div", {
        "class": arrowClasses.value,
        "x-placement": "right",
        "onClick": onNext
      }, [dir.value === 'h' ? createVNode(FeatherChevronLeft, {
        "rotate": 180
      }, null) : createVNode(FeatherChevronLeft, {
        "rotate": -90
      }, null)])]
    }) : null]), createVNode("div", {
      "class": "cm-carousel-list",
      "ref": list,
      "style": listStyle.value
    }, [loop.value && store.data.length ? createVNode(InnerItem, {
      "index": -1,
      "data": store.data[store.data.length - 1]
    }, null) : null, store.data.map((item, index) => {
      return createVNode(InnerItem, {
        "key": item.id,
        "index": index,
        "data": item
      }, null);
    }), loop.value && store.data.length ? createVNode(InnerItem, {
      "index": store.data.length,
      "data": store.data[0]
    }, null) : null])]);
  }
});
Carousel.Item = CarouselItem;

export { CarouselContextKey, Carousel as default };
